package xyj.sparrow.discovery.zk.bean.strategies;

import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.x.discovery.ServiceCache;
import org.apache.curator.x.discovery.ServiceInstance;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * 监听服务实例变化 此策略使用主策略来选择初始实例。 一旦选择，该实例总是被返回。 但是，如果当前选择的实例不再在列表中，则使用顺序策略选择一个新实例。
 */
@Slf4j

public class StickyServiceListener implements ServiceListener {
    private List<ServiceInstance<Object>> instances;
    private final ServiceCache<Object> serviceCache;
    private final String serviceName;
    private final AtomicReference<ServiceInstance<Object>> ourInstance =
            new AtomicReference<>(null);
    private final AtomicInteger index = new AtomicInteger(0);
    private final Consumer<Object> consumer;


    public StickyServiceListener(ServiceCache<Object> serviceCache, List<ServiceInstance<Object>> instances,
                                 String serviceName, Consumer<Object> consumer) {
        this.serviceCache = serviceCache;
        this.instances = instances;
        this.serviceName = serviceName;
        this.consumer = consumer;
    }

    /**
     * 服务实例发生改变
     *
     * @return void
     * @author 江南小俊
     * @since 2021/6/19 下午10:48
     **/

    @Override
    public void cacheChanged() {
        log.info("{}服务实例发生变化", serviceName);
        this.instances = serviceCache.getInstances();
        if (consumer != null) {
            consumer.accept(instances);
        }
    }

    @Override
    public void stateChanged(CuratorFramework client, ConnectionState newState) {

    }

    /**
     * 获取实例
     *
     * @return org.apache.curator.x.discovery.ServiceInstance<java.lang.String>
     * @author 江南小俊
     * @since 2021/6/19 下午11:42
     **/
    @Override
    public ServiceInstance<Object> getInstance() throws Exception {

        if (instances.isEmpty()) {
            //拉一次
            this.instances = serviceCache.getInstances();
            if (instances.isEmpty()) {
                return null;
            }
        }

        ServiceInstance<Object> localOurInstance = ourInstance.get();
        if (!instances.contains(localOurInstance)) {
            ourInstance.compareAndSet(localOurInstance, null);
        }


        if (ourInstance.get() == null) {
            int thisIndex = Math.abs(index.getAndIncrement());
            ServiceInstance<Object> instance = instances.get(thisIndex % instances.size());
            ourInstance.compareAndSet(null, instance);
        }
        return ourInstance.get();
    }

    @Override
    public List<ServiceInstance<Object>> listInstance() {
        return instances;
    }
}